package br.com.firula.dao.hibernate;

import java.util.Calendar;
import java.util.Date;
import java.util.Iterator;
import java.util.List;

import org.hibernate.Criteria;
import org.hibernate.HibernateException;
import org.hibernate.Session;
import org.hibernate.criterion.Example;
import org.hibernate.criterion.Example.PropertySelector;
import org.hibernate.criterion.Expression;
import org.hibernate.criterion.LogicalExpression;
import org.hibernate.criterion.MatchMode;
import org.hibernate.criterion.Order;
import org.hibernate.criterion.Projections;
import org.hibernate.type.Type;
import org.springframework.orm.ObjectRetrievalFailureException;
import org.springframework.orm.hibernate3.support.HibernateDaoSupport;

import br.com.firula.dao.GenericDAO;
import br.com.firula.to.BaseTO;
import br.com.firula.web.filter.Filtro;

public class GenericDAOHibernate extends HibernateDaoSupport implements GenericDAO, PropertySelector {

	private static final long serialVersionUID = 8526408252203606024L;
	
	private boolean filtraAtivos = false;
	
	@SuppressWarnings("unchecked")
	public <T> List<T> findAll(Class<T> entityClass) {
		List<T> results = getHibernateTemplate().loadAll(entityClass);
		return results;
	}
	
	@SuppressWarnings("unchecked")
	public <T> T findById(Class<T> entityClass, Long id) {
		T o = (T) getHibernateTemplate().get(entityClass, id);
		if(o == null){
			throw new ObjectRetrievalFailureException(entityClass, id);
		} else {
			return o;
		}
	}
	
	public <T> void remove(T entity) {
		getHibernateTemplate().delete(entity);
	}

	public <T> void saveOrUpdate(T entity) {
		getHibernateTemplate().saveOrUpdate(entity);
	}
	
	public <T> void update(T entity) {
		getSession().clear();
		getHibernateTemplate().update(entity);
	}

	public <T> Integer countAll(Class<T> entityClass) {
		Session s= getSession();
		Criteria c = s.createCriteria(entityClass, "select count(*)");
    	
		c.setProjection(Projections.projectionList().add(Projections.rowCount()));
		try {
			Integer i = null;
			List l = (List)c.list();
			Iterator iter = l.iterator();
			while (iter.hasNext()) {
				i = (Integer)iter.next();
				break;
			}
			return i;
		} catch (HibernateException e) {
			e.printStackTrace();
		}
		
		return 0;
	}

	@SuppressWarnings("unchecked")
	public <T> List<T> findByExample(final T example) {
        try {
            final Session s = getSession(false);
            final Criteria c = s.createCriteria(example.getClass());
            c.add(Example.create(example).enableLike(MatchMode.ANYWHERE).ignoreCase().setPropertySelector(this));
            return c.list();
        } catch (final HibernateException ex) {
            throw convertHibernateAccessException(ex);
        }
    }
	
	public <T> List findByExample(T example, int first, int max) {
		try {
            final Session s = getSession(false);
            final Criteria c = s.createCriteria(example.getClass());
            c.add(Example.create(example).enableLike(MatchMode.ANYWHERE).ignoreCase().setPropertySelector(this));
            addOrderToCriteria(c);
            addPropertiedToCriteria(c, example);
            if (first != 0) {
                c.setFirstResult(first);
            }
            if (max != 0) {
                c.setMaxResults(max);
            }
            return c.list();
        } catch (final HibernateException ex) {
            throw convertHibernateAccessException(ex);
        }
	}
	
	public <T> List findByExample(T example[], Date inicio, Date fim, int first, int max, String order) {
		try {
            final Session s = getSession(false);
            Criteria c = s.createCriteria(example[0].getClass());
            
            int index = 0;
            if (example.length > 1){
            	LogicalExpression base = Expression.or(Example.create(example[index++]).enableLike(MatchMode.ANYWHERE).ignoreCase().setPropertySelector(this),
            			Example.create(example[index++]).enableLike(MatchMode.ANYWHERE).ignoreCase().setPropertySelector(this));
            	
	            while (example.length > index){
	            	base = Expression.or(base, 
	            			Example.create(example[index++]).enableLike(MatchMode.ANYWHERE).ignoreCase().setPropertySelector(this));
	            }
	            c.add(base);
            } else {
            	c.add(Example.create(example[0]).enableLike(MatchMode.ANYWHERE).ignoreCase().setPropertySelector(this));
            }
            
            if(order!=null){
            	if(order.contains(" desc")) {
            		order = order.replace(" desc", "");
            		if (!order.isEmpty()) {
            			c.addOrder(Order.desc(order));
            		}
            	} else {
            		c.addOrder(Order.asc(order));
            	}
            }
            
            if(fim != null) {
	            Calendar cal = Calendar.getInstance();
				cal.setTime(fim);
				cal.add(Calendar.DAY_OF_YEAR, 1);
				fim = cal.getTime();
			}
			
            if(inicio != null)
            	c.add(Expression.ge("dataCriacao", inicio));
            if(fim != null)
            	c.add(Expression.le("dataCriacao", fim));
            
            addOrderToCriteria(c);
            addPropertiedToCriteria(c, example);
            
            if (first != 0) {
                c.setFirstResult(first);
            }
            if (max != 0) {
                c.setMaxResults(max);
            }
            return c.list();
        } catch (final HibernateException ex) {
            throw convertHibernateAccessException(ex);
        }
	}
	
	public <T> int countByExample(T example) {
		Session s = getSession();
		Criteria c = s.createCriteria(example.getClass(), "select count(*)");
    	
		c.setProjection(Projections.projectionList().add(Projections.rowCount()));
		c.add(Example.create(example).enableLike(MatchMode.ANYWHERE).ignoreCase().setPropertySelector(this));
		try {
			Integer i = null;
			List l = (List)c.list();
			Iterator iter = l.iterator();
			while (iter.hasNext()) {
				i = (Integer)iter.next();
				break;
			}
			return i;
		} catch (HibernateException e) {
			e.printStackTrace();
		}
		return 0;
	}
	
	public <T> int countByExample(T example[],Date inicio, Date fim) {
		
		try {
            final Session s = getSession(false);
            Criteria c = s.createCriteria(example[0].getClass(), "select count(*)");
            c.setProjection(Projections.projectionList().add(Projections.rowCount()));
            
            if(fim != null) {
	            Calendar cal = Calendar.getInstance();
				cal.setTime(fim);
				cal.add(Calendar.DAY_OF_YEAR, 1);
				fim = cal.getTime();
			}
			if(inicio!=null)
            	c.add(Expression.ge("dataCriacao",inicio));
            if(fim!=null)
            	c.add(Expression.le("dataCriacao",fim));
            
            
            int index = 0;
            if (example.length > 1){
            	LogicalExpression base = Expression.or(Example.create(example[index++]).enableLike(MatchMode.ANYWHERE).ignoreCase().setPropertySelector(this),
            			Example.create(example[index++]).enableLike(MatchMode.ANYWHERE).ignoreCase().setPropertySelector(this));
            	
	            while (example.length > index){
	            	base = Expression.or(base, 
	            			Example.create(example[index++]).enableLike(MatchMode.ANYWHERE).ignoreCase().setPropertySelector(this));
	            }
	            c.add(base);
            } else {
            	c.add(Example.create(example[0]).enableLike(MatchMode.ANYWHERE).ignoreCase().setPropertySelector(this));
            }
            
//            addOrderToCriteria(c);
            addPropertiedToCriteria(c, example);

            Integer i = 0;
			List l = (List)c.list();
			Iterator iter = l.iterator();
			while (iter.hasNext()) {
				i = (Integer)iter.next();
				break;
			}
			return i;
        } catch (final HibernateException ex) {
            throw convertHibernateAccessException(ex);
        }
	}

	public boolean include(Object propertyValue, String propertyName, Type type) {
		if((propertyValue!=null) && (propertyValue instanceof String)){
            return !"".equals(((String)propertyValue).trim());
        }
        return propertyValue!=null;
	}

	public void addOrderToCriteria(Criteria c) {
	}
	
	public void addPropertiedToCriteria(Criteria c, Object o) {
	}
	
	public <T> List<T> findByExampleRemovidoFalse(T example) {
        try {
            final Session s = getSession(false);
            final Criteria c = s.createCriteria(example.getClass());
            c.add(Example.create(example).enableLike(MatchMode.ANYWHERE).ignoreCase().setPropertySelector(this));
            addPropertiedToCriteria(c, example);
            return c.list();
        } catch (final HibernateException ex) {
            throw convertHibernateAccessException(ex);
        }
    }
	
	public List findByFilter(Filtro filter) {
		if(filter != null){
			
			if(filter.getExample()==null || filter.getExample().length==0)
				filter.createBlankExample();
			
			if(filter.getOrder()==null)
				filter.setOrder(getOrdemPadrao());
			
			
			return findByExample(filter.getExample(), filter.getDataCriacaoInicio(), filter.getDataCriacaoTermino(), filter.getFirst(), filter.getPageSize(),filter.getOrder());
			
		}else{
			
			return findByExample(new BaseTO[0],null,null, 0, 0, null);
		}
//        try {
//            final Session s = getSession(false);
//            final Criteria c = s.createCriteria(filter.getClazz());
//            if (filter.getExample() != null) {
//            	c.add(Example.create(filter.getExample()).enableLike(MatchMode.ANYWHERE).ignoreCase().setPropertySelector(this));
//            	addPropertiedToCriteria(c, filter.getExample());
//            }
//            c.setFirstResult(filter.getFirst());
//            c.setMaxResults(filter.getPageSize());
//            
//            if(filter.getOrder() != null)
//            	c.addOrder(Order.asc(filter.getOrder()));
//            
//            return c.list();
//        } catch (final HibernateException ex) {
//            throw convertHibernateAccessException(ex);
//        }
	}
	public int countByFilter(Filtro filter) {
		
		return countByExample(filter.getExample(),filter.getDataCriacaoInicio(), filter.getDataCriacaoTermino());
		
//        try {
//            final Session s = getSession(false);
//            Criteria c = s.createCriteria(filter.getClazz(), "select count(*)");
//            c.setProjection(Projections.projectionList().add(Projections.rowCount()));
//            if (filter.getExample() != null) {
//            	c.add(Example.create(filter.getExample()).enableLike(MatchMode.ANYWHERE).ignoreCase().setPropertySelector(this));
//            	addPropertiedToCriteria(c, filter.getExample());
//            }
//            
//            Integer i = 0;
//			List l = (List)c.list();
//			Iterator iter = l.iterator();
//			while (iter.hasNext()) {
//				i = (Integer)iter.next();
//				break;
//			}
//            return i;
//        } catch (final HibernateException ex) {
//            throw convertHibernateAccessException(ex);
//        }
	}
	public String getOrdemPadrao(){
		return "id";
	}

	public boolean isFiltraAtivos() {
		return filtraAtivos;
	}

	public void setFiltraAtivos(boolean filtraAtivos) {
//		System.out.println("GenericDAOHibernate.setFiltraAtivos();");
//		System.out.println("filtra: "+filtraAtivos);
		this.filtraAtivos = filtraAtivos;
//		if(filtraAtivos){
//			getSession().enableFilter("ativoTrue").setParameter("ativoParam", true);
//			getHibernateTemplate().enableFilter("ativoTrue").setParameter("ativoParam", true);
//		}
//		
	}
}
